import requests
from requests import RequestException
from zeus.operation_service.app.core.common.utils import SingletonMeta
from vulcanus.log.log import LOGGER

BASE_URL = "http://{server}/api/v1/{type}"
METRIC_PREFIX = "gala_gopher"


def _metric_name_format(metric_name: str, labels: dict):
    labels_str = '{'
    for key, value in labels.items():
        labels_str = f'{labels_str}{key}="{value}",'
    labels_str += '}'
    return f"{METRIC_PREFIX}_{metric_name}{labels_str}" if metric_name else f"{labels_str}"


def _url_params_format(url: str, params: dict):
    params_str = ''
    for key, value in params.items():
        params_str += f"{key}={value}&"
    return f"{url}?{params_str}"


class PrometheusCollector(metaclass=SingletonMeta):
    def __init__(self, server):
        self.server = server

    def _get_data(self, query_type: str, query_params: dict):
        query_url = BASE_URL.format(server=self.server, type=query_type)
        try:
            response = requests.get(query_url, query_params).json()
            if response.get('status') == 'error':
                LOGGER.error(f"<{_url_params_format(query_url, query_params)}> return error: {response.get('error')}")
        except (RequestException, Exception):
            LOGGER.error(f"<{_url_params_format(query_url, query_params)}> get failed")
            return None
        return response.get('data')

    def get_instant_metrics(self, metric_name: str, labels: dict = {},
                            start_time: int = None) -> list:
        """
        获取瞬时指标数据
        输入：
            metric_name: 指标名称, 不提供默认返回满足标签特征的所有类型指标数据
            labels: 以字典形式提供指标标签信息, 标签之间是and关系
            start_time: 查询的时间戳，默认当前系统时间
        输出：
            出错或没有数据返回None
            成功返回某个时间点满足标签特征的指标数据列表
            格式：
            [
              {
                "metric": { "<label_name>": "<label_value>", ... },
                "value": [ <unix_time>, "<sample_value>" ]
              },
              ...
            ]
        """
        query_params = {'query': _metric_name_format(metric_name, labels)}
        if start_time:
            query_params['time'] = start_time

        data: dict = self._get_data('query', query_params)
        return data.get('result') if data else None

    def get_range_metrics(self, metric_name: str, labels: dict = {}, start_time: int = None,
                          end_time: int = None, step: int = 5) -> list:
        """
        获取区间指标数据
        输入：
            metric_name: 指标名称, 不提供默认返回满足标签特征的所有类型指标数据
            labels: 以字典形式提供指标标签信息, 标签之间是and关系
            start_time: 开始时间戳
            end_time: 结束时间戳
            step: 查询时间步长，时间区间内每 step 秒执行一次
        输出：
            出错或没有数据返回None
            成功返回时间区间内满足标签特征的指标数据列表
            格式：
            [
              {
                "metric": { "<label_name>": "<label_value>", ... },
                "values": [ [ <unix_time>, "<sample_value>" ], ... ]
              },
              ...
            ]
        """
        if not start_time or not end_time:
            LOGGER.error("Must spicify start_time and end_time")
            return None
        query_params = {
            'query': _metric_name_format(metric_name, labels),
            'start': start_time,
            'end': end_time,
            'step': step,
        }
        data: dict = self._get_data('query_range', query_params)
        return data.get('result') if data else None

    def get_series(self, metric_name: str, labels: dict = {}, start_time: int = None,
                   end_time: float = None) -> list:
        """
        通过标签选匹配查询时间序列元数据列表
        输入：
            metric_name: 指标名称
            labels: 以字典形式提供指标标签信息, 标签之间是and关系
            start_time: 开始时间戳, 不提供时间戳默认是最近5分钟的数据
            end_time: 结束时间戳
        输出：
            出错或没有数据返回None
            成功返回对象列表，这些对象包含标识每个系列的标签名称/值对
            格式：
            [
                { "<label_name>": "<label_value>", ... },
                { "<label_name>": "<label_value>", ... },
              ...
            ]
        """
        query_params = {
            'match[]': _metric_name_format(metric_name, labels),
        }
        if start_time and end_time:
            query_params['start'] = start_time
            query_params['end'] = end_time
        data: list = self._get_data('series', query_params)
        return data if data else None
